package com.wh.wisdomsite.xbox.utils.calculate;

import java.math.BigDecimal;
import java.text.DecimalFormat;

import com.wh.wisdomsite.xbox.utils.MathUtils;
import com.wh.wisdomsite.xbox.utils.convert.ConvertTool;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * 计算工具类
 * @Package com.wh.wisdomsite.xbox.utils.calculate
 * @author 谢泽鹏
 * @date 2014年10月8日 下午4:02:14
 * @Copyright 个人版权所有
 * @Company 贵州万华科技有限公司Copyright (c) 2014
 * @version V1.0
 */
public class CalculateTool {

    private static final Log logger = LogFactory.getLog(CalculateTool.class);

    //计算保留2位精度
    private static final int DEFAULT_SCALE_2 = 2;

    //运算参数1
    public static BigDecimal val_1 = null;

    //运算参数2
    public static BigDecimal val_2 = null;


    /**
     * 精确累加
     * @param vals 累加数组
     * @return 累加后值
     */
    public static String add(Object... vals) {
        BigDecimal result = new BigDecimal("0");
        for (Object val : vals){
            BigDecimal bs = new BigDecimal(ConvertTool.convert(val));
            result = result.add(bs);
        }
        return result.toString();
    }


    /**
     * 精确加法
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static double add(double v1, double v2) {
        val_1 = new BigDecimal(Double.parseDouble(v1 + ""));
        val_2 = new BigDecimal(Double.parseDouble(v2 + ""));
        return val_1.add(val_2).doubleValue();
    }


    /**
     * 精确加法
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(BigDecimal v1, BigDecimal v2) {
        val_1 = v1;
        val_2 = v2;
        return val_1.add(val_2);
    }


    /**
     * 精确减法
     * @param v1 减数
     * @param v2 被减数
     * @return 两个参数的差
     */
    public static double substract(double v1, double v2) {
        val_1 = new BigDecimal(Double.toString(v1));
        val_2 = new BigDecimal(Double.toString(v2));
        return val_1.subtract(val_2).doubleValue();
    }


    /**
     * 精确减法
     * @param v1 减数
     * @param v2 被减数
     * @return 两个参数的差
     */
    public static double substract(BigDecimal v1, BigDecimal v2) {
        val_1 = v1;
        val_2 = v2;
        return val_1.subtract(val_2).doubleValue();
    }

    /**
     * 精确减法
     * @param v1 减数
     * @param v2 被减数
     * @return 两个参数的差
     */
    public static String subtract(Object v1, Object v2) {
        val_1 = new BigDecimal(ConvertTool.convert(v1));
        val_2 = new BigDecimal(ConvertTool.convert(v2));
        return val_1.subtract(val_2).toString();
    }



    /**
     * 精确累乘
     * @param vals 累乘数组
     * @return 累乘后值
     */
    public static String multiply(Object... vals) {
        BigDecimal result = new BigDecimal("1");
        for(Object val : vals){
            BigDecimal ba = new BigDecimal(ConvertTool.convert(val));
            result = result.multiply(ba);
        }
        return result.toString();
    }


    /**
     * 精确乘法
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static double multiply(double v1, double v2) {
        val_1 = new BigDecimal(Double.toString(v1));
        val_2 = new BigDecimal(Double.toString(v2));
        return val_1.multiply(val_2).doubleValue();
    }


    /**
     * 精确乘法
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static double multiply(BigDecimal v1, BigDecimal v2) {
        val_1 = v1;
        val_2 = v2;
        return val_1.multiply(val_2).doubleValue();
    }


    /**
     * 精确除法（相对）精确到2位后，四舍五入
     * @param v1 除数
     * @param v2 被除数
     * @return 两个参数的商
     */
    public static double divide(double v1, double v2) {
        return divide(v1, v2, DEFAULT_SCALE_2);
    }


    /**
     * 精确除法（相对）精确到xx位后，四舍五入
     * @param v1 除数
     * @param v2 被除数
     * @param scale 精确xx位
     * @return 两个参数的商
     */
    public static double divide(double v1, double v2, int scale) {
        val_1 = new BigDecimal(Double.toString(v1));
        val_2 = new BigDecimal(Double.toString(v2));
        return val_1.divide(val_2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }



    /**
     * 精确除法（相对）精确到2位后，四舍五入
     * @param v1 除数
     * @param v2 被除数
     * @return 两个参数的商
     */
    public static String divide(Object v1, Object v2) {
        val_1 = new BigDecimal(ConvertTool.convert(v1));
        val_2 = new BigDecimal(ConvertTool.convert(v2));
        return val_1.divide(val_2, DEFAULT_SCALE_2, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 精确除法（相对）精确到xx位后，四舍五入
     * @param v1 除数
     * @param v2 被除数
     * @param scale 精确xx位
     * @return 两个参数的商
     */
    public static String divide(Object v1, Object v2, int scale) {
        val_1 = new BigDecimal(ConvertTool.convert(v1));
        val_2 = new BigDecimal(ConvertTool.convert(v2));
        return val_1.divide(val_2, scale, BigDecimal.ROUND_HALF_UP).toString();
    }



    /**
     * 精确除法（相对）精确到2位后，四舍五入
     * @param v1 除数
     * @param v2 被除数
     * @return 两个参数的商
     */
    public static BigDecimal divide(BigDecimal v1, BigDecimal v2) {
        return divide(v1, v2, DEFAULT_SCALE_2);
    }


   /**
     * 精确除法（相对）精确到xx位后，四舍五入
     * @param v1 除数
     * @param v2 被除数
     * @param scale 精确xx位
     * @return 两个参数的商
     */
    public static BigDecimal divide(BigDecimal v1, BigDecimal v2, int scale) {
        val_1 = v1;
        val_2 = v2;
        return val_1.divide(val_2, scale, BigDecimal.ROUND_HALF_UP);
    }


    /**
     * 精确小数
     * @param v1 精确数字
     * @param scale 精确xx位
     * @return 四舍五入后的结果
     */
    public static double round(double v1, int scale) {
        val_1 = new BigDecimal(Double.toString(v1));
        val_2 = new BigDecimal("1");
        return val_1.divide(val_2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }


    /**
     * 获取数组最小值
     * @param bigDecimals 精确数字
     * @return 返回结果
     */
    public static BigDecimal getBigDecimalMin(BigDecimal[] bigDecimals){
        BigDecimal bigDecimalMin = bigDecimals[0];
        for (BigDecimal bigDecimal : bigDecimals) {
            if(bigDecimalMin.compareTo(bigDecimal) > 0){
                bigDecimalMin = bigDecimal;
            }
        }
        return bigDecimalMin;
   }


    /**
     * 获取数组最大值
     * @param bigDecimals 精确数字
     * @return 返回结果
     */
    public static BigDecimal getBigDecimalMax(BigDecimal[] bigDecimals){
        BigDecimal bigDecimalMax = bigDecimals[0];
        for (BigDecimal bigDecimal : bigDecimals) {
            if(bigDecimalMax.compareTo(bigDecimal) > 0){
                bigDecimalMax = bigDecimal;
            }
        }
        return bigDecimalMax;
    }

    /**
     * 两点间的距离
     * @param x1 点1的x坐标
     * @param y1 点1的y坐标
     * @param x2 点2的x坐标
     * @param y2 点2的y坐标
     * @return
     */
    public static double distance(double x1, double y1, double x2, double y2) {
        return Math.sqrt(Math.abs(x1 - x2) * Math.abs(x1 - x2) + Math.abs(y1 - y2) * Math.abs(y1 - y2));
    }


    /**
     * 计算点a(x,y)的角度
     * @param x
     * @param y
     * @return
     */
    public static double pointTotoDegrees(double x, double y){
        return Math.toDegrees(Math.atan2(x,y));
    }


    /**
     * 点在圆肉
     * @param sx
     * @param sy
     * @param r
     * @param x
     * @param y
     * @return
     */
    public static boolean checkInRound(float sx, float sy, float r, float x, float y) {
        return Math.sqrt((sx - x) * (sx - x) + (sy - y) * (sy - y)) < r;
    }


    /**
     * 用','将数字分割
     * @param num
     * @return
     */
    public static String splitNum(Object num){
        DecimalFormat df = new DecimalFormat("#,###");
        return df.format(num);
    }


    /**
     * 金额分割，四舍五人金额
     * @param s
     * @return
     */
    public static String formatMoney(BigDecimal s){
        String retVal = "";
        String str = "";
        boolean is_positive_integer = false;
        if(null == s){
            return "0.00";
        }

        if(0 == s.doubleValue()){
            return "0.00";
        }
        //判断是否正整数
        if(s.toString().indexOf("-") != -1){
            is_positive_integer = true;
        }else{
            is_positive_integer = false;
        }
        //是负整数
        if(is_positive_integer){
            //去掉 - 号
            s = new BigDecimal(s.toString().substring(1, s.toString().length()));
        }
        str = s.setScale(2, BigDecimal.ROUND_HALF_UP).toString();
        StringBuffer sb = new StringBuffer();
        String[] strs = str.split("\\.");
        int j = 1;
        for(int i = 0; i < strs[0].length(); i++){
              char a = strs[0].charAt(strs[0].length()-i-1);
              sb.append(a);
              if(j % 3 == 0 && i != strs[0].length()-1){
                  sb.append(",");
              }
              j++;
        }
        String str1 = sb.toString();
        StringBuffer sb1 = new StringBuffer();
        for(int i = 0; i < str1.length(); i++){
              char a = str1.charAt(str1.length()-1-i);
              sb1.append(a);
        }
            sb1.append(".");
            sb1.append(strs[1]);
            retVal = sb1.toString();

       if(is_positive_integer){
                retVal = "-" + retVal;
       }
            return retVal;
    }



    /**
     * 四舍五人金额
     * @param s
     * @return
     */
    public static String formatMoney1(BigDecimal s){
        String retVal = "";
        String str = "";
        boolean is_positive_integer = false;
        if(null == s){
            return "0.00";
        }

        if(0 == s.doubleValue()){
            return "0.00";
        }
        //判断是否正整数
        if(s.toString().indexOf("-") != -1){
            is_positive_integer = true;
        }else{
            is_positive_integer = false;
        }
        //是负整数
        if(is_positive_integer){
            //去掉 - 号
            s = new BigDecimal(s.toString().substring(1, s.toString().length()));
        }
        str = s.setScale(2, BigDecimal.ROUND_HALF_UP).toString();
        StringBuffer sb = new StringBuffer();
        String[] strs = str.split("\\.");
        int j = 1;
        for(int i = 0; i < strs[0].length(); i++){
              char a = strs[0].charAt(strs[0].length()-i-1);
              sb.append(a);
              if(j % 3 == 0 && i != strs[0].length()-1){
                  sb.append("");
              }
              j++;
        }
        String str1 = sb.toString();
        StringBuffer sb1 = new StringBuffer();
        for(int i = 0; i < str1.length(); i++){
              char a = str1.charAt(str1.length()-1-i);
              sb1.append(a);
        }
            sb1.append(".");
            sb1.append(strs[1]);
            retVal = sb1.toString();

       if(is_positive_integer){
                retVal = "-" + retVal;
       }
            return retVal;
    }



    /**
     * 按天计算获取还款总额(天标)
     * @param capital  借款本金
     * @param day      借款天数
     * @param yearRate 年利率(百分比)
     * @return
     * 备注
     * 1.按天利率计算，投资收益 = 投资额 * 天利率 * 天数。
     * 2.按年利率计算，投资收益 = 投资额 * 年利率/360 * 天数 + 投资额 * 奖励比例
     */
    public static double dayMoney(double capital, double day, double yearRate){
        logger.info("按(dayMoney)还款方式计算->>开始");
        logger.info("dayMoney->>本金:" + capital + ", 天数: " + day + ", 年利率: " + yearRate);
        //天利率(年利率/365天)
        double dayRate = yearRate / 365;
        logger.info("dayMoney->>天利率: " + dayRate);
        //总共利息(天利率 * 本金 * 借款天数)
        double allInterest = dayRate * capital * day;
        //还款总额(基本本金 + 总共利息)
        double repayTotal = capital + allInterest;
        logger.info("dayMoney->>总共利息: " + allInterest);
        logger.info("dayMoney->>还款总额: " + repayTotal);
        logger.info("按(dayMoney)还款方式计算->>结束");
        return repayTotal;
    }


    /**
     * 按月结算每月还款金额
     * @param capital    借款本金
     * @param yearRate   年利率(百分比)
     * @param repayMonth 还款月数
     * 备注
     * 1.按年利率计算，投资收益 = 投资额 * 年利率/360 * 天数 + 投资额 * 奖励比例
     */
    public static double Mrpi(double capital, double yearRate, int repayMonth){
        logger.info("按(Mrpi)还款方式计算->>开始");
        logger.info("dayMoney->>本金:" + capital + ", 年利率: " + yearRate + ", 还款月数: " + repayMonth);

        //月利息(年利率 / 12)
        double monthRate = yearRate / 12;
        //总共利息(每天利息 * 本金 * 借款天数)
        double aprPow = Math.pow(1 + monthRate, repayMonth);
        double monPay = capital * monthRate * aprPow / (aprPow - 1);
        logger.info("Mrpi->>月利息: " + monthRate);
        logger.info("Mrpi->>每月利息2: " + aprPow);
        logger.info("Mrpi->>每月利息3: " + monPay);
        logger.info("按(Mrpi)还款方式计算->>结束");
        return monPay;

    }




    /**
     *
     * 三个变量x,y,z
     *      x: 代表提现金额
     *      y: 代表现在净资产减15天内的充值总值
     *      z: 代表提现手续费
     *      1.  0 ≤ x ≤ 1500 或者 y<15000
     * 无论y为何值    z=0.002x
     *       2.  y≥x
     *      1500<x ≤30000     z=3
     *       30000<x ≤50000    z=5
     *       3. y <x
     *       1500<y ≤30000     z=3+(x-y)0.002
     *       30000<y ≤50000    z=5+(x-y)0.002
     * @param x x代表提现金额
     * @param y y免费额度
     * @param r r代表提现手续费率
     * @param maxCash
     * @return 提现手续费
     */
    public static double GetCashFee(double x,double y,double r, double maxCash){
        if(x <= 1500 || y <= 1500){
            return r * x;
        }else if(y>=x){
            if(x>1500&&x<=30000){
                return 3.0;
            }else{
                return 5;
            }
        }else{
            if(y<=30000){
                return 3+(x-y)*r;
            }else{
                return 5+(x-y)*r;
            }
        }
    }


    public static double GetCashFeeForAidai(double x,double y,double r,double maxCash){
        if (y<=0) {
            return 0;
        } else if (y<=x) {
            return x*r;
        } else {
            return (x-y)*r;
        }
    }

    /**
     * 招商贷体现规则（充值15天内取现千三手续费，15天后免费）
     * y：免费提现额度
     * x：提现金额
     */
    public static double cashFeeForZsd(double x,double y,double r,double maxCash){
        if(y<=0){
            return 0;
        } else {
            return r*x;
        }
    }

    /**
     * 招商贷提现规则
     */
    public static double GetZsdCashFee(double x,double y,double r,double maxCash){
        if (y <= 0) {
            return r * x;
        } else if (y >= x) {
            return 0;
        } else {
            return (x - y) * r;
        }
    }


    /**
     * 还款方式：按月等额还本付息，计算每月还款数
     * @param p 本金
     * @param r 年利率
     * @param mn 还款月数
     * @return  每月还款金额
     */
    public static BigDecimal Mrpi(BigDecimal p,BigDecimal r,int mn){
        //月利率（精确除法到8位小数计算）
        //月利率 = 年利率 / 12个月  = 0.01166667
        BigDecimal mr = MathUtils.divide(r,12,8);
        BigDecimal aprPow = MathUtils.add(1,mr).pow(mn);
        BigDecimal monPay = MathUtils.divide(MathUtils.multiply(p,mr,aprPow), MathUtils.subtract(aprPow,1), 8);
        return monPay;
    }

    /**
     * 还款方式：按月付息到期还本， 计算：月利息金额
     * @param money 投资本金
     * @param rate  月利率
     * @param mn    期数
     */
    public static BigDecimal MrPInterest(BigDecimal money, BigDecimal rate, int mn){
        //月利率（精确除法到8位小数计算）
        //月利率 = 年利率 / 12个月  = 0.01166667
        BigDecimal mr = MathUtils.divide(rate, 12, 8);

        //月利息（精确乘法到2位小保留）
        //月利息 = 投资本金  * 月利息
        BigDecimal monPay = MathUtils.multiply(money, mr).setScale(2, BigDecimal.ROUND_HALF_UP);

        //返回月利息金额
        return monPay;
    }












    public static void main(String[] args) {
         System.out.println("金额0：         " + formatMoney1(new BigDecimal(0)));
         System.out.println("金额0.0 ：         " + formatMoney1(new BigDecimal(0.0)));
         System.out.println("金额0.00：         " + formatMoney1(new BigDecimal(0.00)));
         System.out.println("金额0.58：         " + formatMoney1(new BigDecimal(0.58)));
         System.out.println("金额5.58：         " + formatMoney1(new BigDecimal(5.58)));
         System.out.println("金额5.54：          " + formatMoney1(new BigDecimal(5.54)));
         System.out.println("金额512322.555555111：          " + formatMoney1(new BigDecimal(512322.555555111)));
         System.out.println("金额3423423425.54：     " + formatMoney1(new BigDecimal(3423423425.54)));
         System.out.println("金额3423423425.58：      " + formatMoney1(new BigDecimal(3423423425.58)));
         System.out.println("金额1000000.543453：     " + formatMoney1(new BigDecimal(1000000.543453)));
         System.out.println("金额9343788754.573453：     " + formatMoney1(new BigDecimal(-9343788754.573453)));
         System.out.println("金额9343788756.577：     " + formatMoney1(new BigDecimal(-9343788756.577)));
         System.out.println("金额-343788756.577：     " + formatMoney1(new BigDecimal(-343788756.577)));
         System.out.println("金额-34756.54：     " + formatMoney1(new BigDecimal(-34756.54)));
         System.out.println("金额-34756.556：     " + formatMoney1(new BigDecimal(-34756.556)));

        System.out.println("dayMoney() : " + dayMoney(10000, 30, 0.08));
        System.out.println("Mrpi() : " + Mrpi(10000, 0.08, 1));


         //DateUtils.rollDay(new Date(), -15);
         //直接使用浮点数进行计算，得到的结果是有问题的
         //System.out.println(0.01 + 0.05);

         //使用了BigDecimal类进行计算后，可以做到精确计算
         //System.out.println(BigDecimalMoney.add(0.0000000000005, 0.00000001));
    }
}
